package com.hailiao.beidou.message.ui.page.chat

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.d10ng.app.utils.startBaiDuMapMaker
import com.d10ng.app.utils.startGaoDeMapMaker
import com.d10ng.common.calculate.isOnlyChSymbol
import com.d10ng.common.calculate.isOnlyCode
import com.d10ng.common.coordinate.Coordinate
import com.d10ng.common.coordinate.CoordinateSystemType
import com.d10ng.common.coordinate.convert
import com.d10ng.common.transform.encodeGBK
import com.d10ng.compose.model.UiViewModelManager
import com.d10ng.compose.ui.sheet.builder.ActionSheetBuilder
import com.hailiao.beidou.message.bd.Beidou
import com.hailiao.beidou.message.bd.constant.BDStatus
import com.hailiao.beidou.message.bd.constant.TextType
import com.hailiao.beidou.message.data.chat.ChatRepository
import com.hailiao.beidou.message.data.contact.ContactRepository
import com.hailiao.beidou.message.data.db.entity.MessageEntity
import com.hailiao.beidou.message.data.message.MessageRepository
import com.hailiao.beidou.message.data.message.MessageSender
import com.hailiao.beidou.message.service.SendLocationService
import com.hailiao.beidou.message.ui.page.destinations.SendLocationScreenDestination
import com.hailiao.beidou.message.ui.page.navArgs
import com.hailiao.beidou.message.utils.go
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlin.math.ceil

/**
 * 聊天界面
 * @Author d10ng
 * @Date 2024/3/4 10:56
 */
data class ChatScreenNavArgs(
    val number: String,
    // 需要跳转到的消息位置
    val position: Int = 0
)

class ChatScreenViewModel(
    savedStateHandle: SavedStateHandle
): ViewModel() {

    private val navArgs = savedStateHandle.navArgs<ChatScreenNavArgs>()

    // 初始位置
    val initPosition = navArgs.position
    // 对话号码
    val number = navArgs.number

    // 联系人信息
    private val contactFlow = ContactRepository.getFlow(navArgs.number)

    // 是否存在联系人
    @OptIn(ExperimentalCoroutinesApi::class)
    val hasContactFlow = contactFlow.mapLatest { it != null }

    // 对话名称或号码
    @OptIn(ExperimentalCoroutinesApi::class)
    val nameFlow = contactFlow.mapLatest { it?.name ?: navArgs.number }

    // 消息列表
    val messagesFlow = MessageRepository.getPageDataFlow(navArgs.number)

    // 输入内容
    val inputFlow = MutableStateFlow("").apply {
        viewModelScope.launch {
            value = ChatRepository.getDraft(navArgs.number) ?: ""
        }
    }

    // 编码类型
    @OptIn(ExperimentalCoroutinesApi::class)
    val typeFlow = inputFlow.mapLatest {
        when {
            it.isOnlyCode() -> TextType.CODE
            it.isOnlyChSymbol() -> TextType.CHINESE
            else -> TextType.MIX
        }
    }

    // 已输入字节数
    val countFlow = inputFlow.combine(typeFlow) { input, type ->
        when (type) {
            TextType.CODE -> ceil(input.length / 2.0).toInt()
            TextType.CHINESE -> ceil(input.length * 14 / 8.0).toInt()
            TextType.MIX -> input.encodeGBK().size + 1
        }
    }

    // 最大可输入字节数
    @OptIn(ExperimentalCoroutinesApi::class)
    val maxCountFlow = Beidou.cardFlow.mapLatest { it.sendByteSize }

    // 是否有插卡
    private val hasCardFlow = Beidou.cardFlow.map { it.number.isNotEmpty() }
        .stateIn(viewModelScope, SharingStarted.Eagerly, false)

    init {
        // 创建对话框
        viewModelScope.launch {
            ChatRepository.createBy(navArgs.number)
        }
        // 更新草稿
        viewModelScope.launch {
            inputFlow.collectLatest { str ->
                ChatRepository.saveDraft(navArgs.number, str)
            }
        }
    }

    /**
     * 页面在进入时，设置正在对话的用户账号
     */
    @Synchronized
    fun onLaunch() {
        ChatRepository.chattingAccount = navArgs.number
        CoroutineScope(Dispatchers.IO).launch {
            // 清除未读消息
            ChatRepository.clearUnreadCount(navArgs.number)
        }
    }

    /**
     * 页面在跳转后，会触发onDispose，需要清除正在对话的用户账号
     */
    @Synchronized
    fun onDispose() {
        // 清除正在对话的用户账号
        ChatRepository.chattingAccount = null
    }

    /**
     * 更新输入内容
     * @param value String
     */
    fun onUpdateInput(value: String) {
        inputFlow.value = value
    }

    /**
     * 点击消息位置
     * @param loc Location
     */
    fun onClickMessageLocation(loc: MessageEntity.Location) {
        UiViewModelManager.showSheet(ActionSheetBuilder(
            items = setOf("高德地图", "百度地图"),
            onItemClick = { value ->
                val point = Coordinate(loc.latitude, loc.longitude).convert(CoordinateSystemType.WGS84, CoordinateSystemType.GCJ02)
                when (value) {
                    "高德地图" -> startGaoDeMapMaker(point.lat, point.lng)
                    "百度地图" -> startBaiDuMapMaker(point.lat, point.lng)
                    else -> {}
                }
            }
        ))
    }

    /**
     * 点击发送
     */
    fun onClickSend() {
        if (hasCardFlow.value.not()) {
            UiViewModelManager.showErrorNotify("未插卡！")
            return
        }
        if (Beidou.statusFlow.value != BDStatus.CONNECTED) {
            UiViewModelManager.showErrorNotify("信号不好！")
            return
        }
        val content = inputFlow.value
        inputFlow.value = ""
        CoroutineScope(Dispatchers.IO).launch {
            MessageSender.send(number, content)
        }
    }

    /**
     * 点击发送位置
     */
    fun onClickSendLocation() {
        if (Beidou.isConnected().not()) {
            UiViewModelManager.showErrorNotify("北斗设备未连接！")
            return
        }
        if (hasCardFlow.value.not()) {
            UiViewModelManager.showErrorNotify("未插卡！")
            return
        }
        SendLocationService.targetCard = number
        go(SendLocationScreenDestination.route)
    }
}